package com.yide.myworld.aspect;

import com.yide.myworld.annotation.AssetRecordSave;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author dsw
 * @date 2024/3/11.
 */
@Component
@Aspect
public class AssetRecordSaveAspect {

    // @Autowired
    // AssetRecordMapper assetRecordMapper;

    private static final Logger LOGGER = LoggerFactory.getLogger(AssetRecordSaveAspect.class);

    @Pointcut("@annotation(recordSave)")
    public void assetRecord(AssetRecordSave recordSave) {

    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "assetRecord(recordSave)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, AssetRecordSave recordSave, Object jsonResult) {
        handle(joinPoint, null, jsonResult);
    }

    @AfterThrowing(value = "assetRecord(recordSave)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, AssetRecordSave recordSave, Exception e) {
        handle(joinPoint, e, null);
    }

    public void handle(final JoinPoint joinPoint, final Exception e, Object jsonResult) {
        try {
            AssetRecordSave recordSave = getAnnotation(joinPoint);
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            if (e != null) {
                LOGGER.error("执行添加资产记录切面发生异常:{}", StringUtils.substring(e.getMessage(), 0, 2000));
                throw new Exception(e.getMessage());
            }

            String suffix = recordSave.suffix();
            String value = generateKeyBySpEL(suffix, joinPoint);
            // 记录数据
            // AssetRecord record = JSON.parseObject(value, AssetRecord.class);
            // assetRecordMapper.insert(record);
            LOGGER.info("执行添加资产记录后置通知成功！className:{},methodName:{}", className, methodName);
        } catch (Exception exp) {
            // 记录本地异常日志
            LOGGER.error("==后置通知异常==");
            LOGGER.error("异常信息:{}", exp.getMessage());

            // business_error
            LOGGER.error("添加资产记录发生异常");
        }
    }

    /**
     * 是否存在注解，如果存在就获取
     */
    private AssetRecordSave getAnnotation(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(AssetRecordSave.class);
        }
        return null;
    }

    private SpelExpressionParser parserSpel = new SpelExpressionParser();
    private DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    public String generateKeyBySpEL(String key, JoinPoint pjp) {
        Expression expression = parserSpel.parseExpression(key);
        EvaluationContext context = new StandardEvaluationContext();
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Object[] args = pjp.getArgs();
        String[] paramNames = parameterNameDiscoverer.getParameterNames(methodSignature.getMethod());
        for (int i = 0; i < args.length; i++) {
            context.setVariable(paramNames[i], args[i]);
        }
        return expression.getValue(context).toString();
    }

}
